package com.beantools.reflect.enums;

import com.alibaba.fastjson.JSONObject;
import com.beantools.execption.ClassException;
import com.beantools.execption.PackageException;
import com.beantools.execption.PropertyException;
import com.beantools.util.CollectionUtil;

import java.util.List;
import java.util.Set;

import static com.beantools.constants.RegexExpress.ILLEGAL_CLASS_CHAR;
import static com.beantools.constants.SingleChar.*;
import static com.beantools.entity.enums.TailEnum.TAIL_ENUM;
import static com.beantools.reflect.enums.EnumClassLoader.initialFilterSinglePackageByEnum;
import static com.beantools.reflect.enums.EnumClassJson.assignPropertyJsons;
import static com.beantools.util.ArrayUtil.containNullObject;
import static com.beantools.util.ClassUtil.getClassNameByClass;
import static com.beantools.util.StringUtil.isBlank;
import static com.beantools.util.StringUtil.verifyRegex;
import static java.util.Arrays.copyOf;

/**
 * 这是获取调用者指定的Enum包下的类。
 * <p>
 * 比如，调用者在做<a href='https://baike.baidu.com/item/java%20web/1611944?fr=aladdin'>java-web</a>
 * 开发时。客户端需要获取服务端的枚举类，但是有的枚举是多个字段，有的枚举是单个字段。客户端程序员
 * 还得手动去写枚举类，这无疑是很麻烦；如果服务端的枚举类改变了，那么，客户端还需要手动修改枚举字段。
 * 因而，这个类可以解决这个问题。
 * <p>
 * 你如果用的是spring mvc的开发模式，那么，你可以在配置文件中设置类所在包的路径，然后将路径加载到你的项目中。
 * 一旦你设置好了包所在的路径，那么，客户端只要传以<strong>英文分号</strong>传输的类名称即可。
 * <p>
 * 同时，如果你枚举中的字段比较多，而你指向返回给客户端某些字段，你可以指定哪些字段是返回给客户端的。
 *
 * @author baoya
 * @version 1.0
 * @since 1.1.0
 */

public class EnumPackageJson {

    /**
     * 该方法是用来加载以分号连接的多个包的枚举类并指定哪些枚举类类可以被输出的方法。
     * <p>
     * 如果包含以分号连接的多个包名，那么就进行分割字符串，形成包名数据，然后再调用
     * {@link this#loadEnumMultiPackages(String, String, String...)}方法。
     * <p>
     * 如果调用者指定了哪些枚举类的名称，当且仅当这些枚举类都合法的情况下，会输出这些
     * 枚举类的信息。
     *
     * @param packageNames 以分号连接的java包
     * @param enumNames    以分号连接的枚举类名称
     * @return 返回<code>JSONObject</code>容器对象
     * @throws PackageException  如果包不存在，则会抛出该异常
     * @throws PropertyException 如果是过滤的属性不存在，则会抛出该异常
     */
    public static JSONObject loadEnumMultiPackages(String packageNames, String enumNames) throws PackageException, PropertyException {
        try {
            return loadEnumMultiPackages(packageNames, enumNames, null);
        } catch (PackageException e) {
            throw new PackageException(e.getMessage());
        } catch (PropertyException e) {
            throw new PropertyException(e.getMessage());
        }
    }

    /**
     * 该方法是用来加载以分号连接的多个包的枚举类并指定哪些枚举类的哪些属性可以被输出的方法。
     * <p>
     * 如果包含以分号连接的多个包名，那么就进行分割字符串，形成包名数据，然后再调用
     * {@link this#loadEnumMultiPackages(String, String, String...)}方法。
     * <p>
     * 如果调用者指定了哪些枚举类的名称，当且仅当这些枚举类都合法的情况下，会输出这些枚举类的信息。
     * 如果调用者指定了这些枚举类的属性，即输出枚举类该属性的信息
     *
     * @param packageNames    以分号连接的java包
     * @param enumNames       以分号连接的枚举类名称
     * @param filterPropNames 需要过滤属性的集合
     * @return 返回<code>JSONObject</code>容器对象
     * @throws PackageException  如果包不存在，则会抛出该异常
     * @throws PropertyException 如果是过滤的属性不存在，则会抛出该异常
     */
    public static JSONObject loadEnumMultiPackages(String packageNames, String enumNames, String... filterPropNames) throws PackageException, PropertyException {
        JSONObject var = new JSONObject();
        try {
            if (verifyRegex(packageNames, ILLEGAL_CLASS_CHAR)) {
                throw new IllegalArgumentException("包含非法字符，只能包含英文分号和点号enumNames:" + enumNames);
            }
            String[] split = packageNames.split(EN_SEMICOLON);
//        如果包含空字符，去空
            String[] var1 = new String[split.length];
            int index = 0;
            if (containNullObject(split)) {
                for (String var2 : split) {
                    if (isBlank(var2)) {
                        continue;
                    }
                    var1[index++] = var2;
                }
                var1 = copyOf(var1, index);
            } else {
                var1 = split;
            }
            for (String s : var1) {
                var.putAll(loadEnumSinglePackage(s, enumNames, filterPropNames));
            }
            return var;
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (PackageException e) {
            throw new PackageException(e.getMessage());
        } catch (PropertyException e) {
            throw new PropertyException(e.getMessage());
        }
        return var;
    }

    /**
     * 该方法是用来加载某个包下的枚举类并指定哪些枚举类可以被输出的方法。
     * <p>
     * 如果当前包名是合法的，那么就加载该包下的所有枚举类。
     * 如果调用者指定了哪些枚举类的名称，当且仅当这些枚举类都合法的情况下，会输出这些枚举类的信息。
     *
     * @param packageName 枚举所在包
     * @param enumNames   以分号连接的枚举类名称
     * @return 返回<code>JSONObject</code>容器对象
     * @throws PackageException  如果包不存在，则会抛出该异常
     * @throws PropertyException 如果是过滤的属性不存在，则会抛出该异常
     */
    public static JSONObject loadEnumSinglePackage(String packageName, String enumNames) throws PackageException, PropertyException {
        try {
            return loadEnumSinglePackage(packageName, enumNames, null);
        } catch (PackageException e) {
            throw new PackageException(e.getMessage());
        } catch (PropertyException e) {
            throw new PropertyException(e.getMessage());
        }
    }

    /**
     * 该方法是用来加载某个包下的枚举类并指定哪些枚举类的那些属性可以被输出的方法。
     * <p>
     * 如果<code>classNames</code>和<code>filterPropName</code>为空，或者空字符，则返回该包下的并以特定字符指定的所有枚举类
     * 的所有枚举属性。
     * <p>
     * 如果<code>classNames</code>不为空，但<code>filterPropName</code>为空，或者空字符，则返回该包下的存在于<code>enumNames</code>
     * 中的枚举类的所有字段。
     * <p>
     * 如果<code>classNames</code>和<code>filterPropName</code>均不为空，则返回该包下的存在于<code>enumNames</code>
     * 中的枚举类的指定的字段。
     *
     * @param packageName     调用者指定的包名
     * @param enumNames       枚举类的集合，以分号连接
     * @param filterPropNames 指定过滤的属性名称数组
     * @return 返回<code>jsonObject</code>对象的集合
     * @throws PackageException  如果包不存在，则会抛出该异常
     * @throws PropertyException 如果是过滤的属性不存在，则会抛出该异常
     */
    public static JSONObject loadEnumSinglePackage(String packageName, String enumNames, String... filterPropNames) throws PackageException, PropertyException {
        try {
            if (isBlank(packageName) || isBlank(packageName.replace(BLANK_OP, NULL_OP))) {
                return null;
            }
            Set<Class<?>> classes = initialFilterSinglePackageByEnum(packageName,enumNames, TAIL_ENUM);
            if (verifyRegex(packageName, ILLEGAL_CLASS_CHAR)) {
                throw new IllegalArgumentException("包含非法字符，只能包含英文分号enumNames:" + enumNames);
            }
            JSONObject body = new JSONObject(classes.size());
            if (CollectionUtil.isNotEmpty(classes)) {
                if (isBlank(enumNames)) {
                    for (Class<?> aClass : classes) {
                        if (aClass.isEnum()) {
                            List<JSONObject> jsonObjects = assignPropertyJsons(aClass, filterPropNames);
                            body.put(getClassNameByClass(aClass), jsonObjects.size() <= 0 ? null : jsonObjects);
                        }
                    }
                } else {
                    for (Class<?> aClass : classes) {
                        String[] var1 = enumNames.split(EN_SEMICOLON);
                        if (aClass.isEnum()) {
                            for (String s : var1) {
                                String className = aClass.getSimpleName();
                                if (s.equalsIgnoreCase(className)) {
                                    List<JSONObject> jsonObjects = assignPropertyJsons(aClass, filterPropNames);
                                    body.put(getClassNameByClass(aClass), jsonObjects.size() <= 0 ? null : jsonObjects);
                                }
                            }
                        }
                    }
                }
                return body;
            }
            return body;
        } catch (PackageException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (ClassException e) {
            e.printStackTrace();
        } catch (PropertyException e) {
            e.printStackTrace();
        }
        return null;
    }
}
